﻿using LinqToDB;
using LinqToDB.Data;
using LinqToDB.DataProvider.MySql;
using Model;
using System;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using MySql.Data;
using System.Data.Common;
using LinqToDB.DataProvider.Oracle;
using LinqToDB.SchemaProvider;
using LinqToDB.Mapping;
using Oracle.ManagedDataAccess.Client;
using XunChi.DBHelper;
using System.Data;
using System.Collections.Generic;
using static LinqToDB.Common.Configuration;
using Org.BouncyCastle.Utilities.Collections;
using System.Xml.Linq;

namespace XunChi.LinqDB
{
    public static class Linq2DBHelper
    {
        static string conSqlServerString = ConfigurationManager.ConnectionStrings["SqlServerString"].ConnectionString;
        static string conMySQLString = ConfigurationManager.ConnectionStrings["MySQLServerString"].ConnectionString;
        static string conOracleString = ConfigurationManager.ConnectionStrings["OracleString"].ConnectionString;

        //Oracle的标准连接字符串的写法。其他Server、uid、pwd、Database这些属性都在Oracle连接字符串中不适用的。
        //string connectionString = "Data Source=<your_data_source>;User Id=<your_user>;Password=<your_password>";

        public static int Insert()
        {

            #region 设置输出窗口打印SQL语句必须在DataConnection初始化之前。
            //为设置自定义跟踪侦听器
            //格式化跟踪消息：必须在DataConnection初始化db之前设置，不然没有效果。
            //PrintSQL();
            #endregion

            //必须设置SqlServerVersion的数据库版本号，不然不能正常连接数据库。下面是SQLServer的版本代码。
            //DataOptions dataOptions = new DataOptions().UseSqlServer(conSqlServerString, LinqToDB.DataProvider.SqlServer.SqlServerVersion.v2022);

            //下面是MySql的数据库。
            //DataOptions dataOptions = new DataOptions().UseMySql(conMySQLString);

            //下面是Oracle的数据库
            //Oracle数据库，里面生成的sql不符合MySQL执行的参数化占位符,因为定义变量生成是按照SQLSever的标准生成的。
            DataOptions dataOptions = new DataOptions().UseOracle(conOracleString);

            #region 注释内容
            //MySQL数据库，里面生成的sql不符合MySQL执行的参数化占位符,因为定义变量生成是按照SQLSever的标准生成的。
            /*
             * 在 MySQL 中，DECLARE 关键字确实存在，但它主要用于在存储过程、函数或触发器内部声明局部变量。这些局部变量在存储过程、函数或触发器的生命周期内有效，并且只能在声明它们的代码块内访问。
                然而，在 MySQL 的普通查询会话中（即在您直接连接到 MySQL 服务器并运行 SQL 语句时），您通常不会使用 DECLARE 来声明变量。相反，您会使用用户定义的会话变量，这些变量以 @ 符号开头，并且可以在整个会话期间跨多个查询使用。

                例如，在 MySQL 的查询会话中，您可以这样声明和使用一个会话变量：

                sql
                -- 设置会话变量
                SET @myVariable = 10;
 
                -- 使用会话变量
                SELECT @myVariable + 5;  -- 结果将是 15
                在这个例子中，@myVariable 是一个会话变量，它在整个 MySQL 会话期间都是可用的，直到您显式地更改它的值或关闭会话。

                如果您在存储过程、函数或触发器中工作，那么您应该使用 DECLARE 来声明局部变量，如下所示：

                sql
                DELIMITER //
 
                CREATE PROCEDURE MyProcedure()
                BEGIN
                    DECLARE myLocalVariable INT DEFAULT 0;
                    -- 其他代码，可能包括设置和使用 myLocalVariable
                END //
 
                DELIMITER ;
                在这个存储过程的例子中，myLocalVariable 是一个局部变量，它只能在 MyProcedure 存储过程的代码块内访问和使用。

                总结一下，DECLARE 在 MySQL 中是有效的，但主要用于存储过程、函数和触发器内部声明局部变量。在普通的查询会话中，您应该使用会话变量（以 @ 开头）。
             */
            #endregion

            #region Oracle变量样例
            /*
            下面是修改后的代码，它应该能够正确执行：
            sql
            DECLARE Id NUMBER(22);Name VARCHAR2(100);
            BEGIN
              Id := 1;
              Name:= '张学友'; --注意这里添加了冒号来赋值
              UPDATE STUDENT
              SET NAME = Name
              WHERE ID = Id;
              
             COMMIT;
            END;

            以上虽然可以执行，但是数据库数据数据不改变。因为这里还有一个潜在的问题：在PL/SQL中，当您使用与表列名相同的变量名时（如这里的Name和ID），可能会引起混淆或错误，尽管Oracle通常能够区分它们（通过上下文）。为了避免任何潜在的混淆或未来的维护问题，建议使用不同的变量名，特别是当它们与数据库中的对象名相同时。

            在Oracle数据库中，SQL和PL/SQL对大小写是敏感的，但这种敏感性取决于几个因素，包括对象的创建方式、引用方式以及Oracle的配置。

            对象名的大小写敏感性：
                当创建数据库对象（如表、列、视图等）时，如果对象名用双引号括起来并且包含大写字母，那么Oracle会将该对象名视为大小写敏感的。
            如果对象名没有用双引号括起来，或者虽然用双引号括起来但只包含小写字母（在默认配置下），那么Oracle会将该对象名视为小写，并且在引用时可以不区分大小写。
            SQL语句中的大小写敏感性：
                在SQL语句中引用表名、列名等对象时，如果对象名是在创建时用双引号括起来并且包含大写字母的，那么在引用时必须使用完全相同的大小写和双引号。
                如果对象名是在创建时没有使用双引号或只包含小写字母（在默认配置下），那么引用时可以不区分大小写。
            PL/SQL中的变量名与对象名：
                在PL/SQL中声明的变量名与数据库中的对象名（如表列名）是区分开的。即使它们名字相同（忽略大小写），Oracle也能够通过上下文来区分它们。
                但是，为了避免混淆和潜在的错误，建议不要在PL/SQL中使用与数据库对象名相同（或相似）的变量名。
            关于NAME和Name：
             如果STUDENT表中有一个列名为NAME（假设它是在没有双引号的情况下创建的），那么在SQL语句中引用它时可以不区分大小写（即NAME、name、NaMe等都是等价的，除非在创建时用了双引号并且包含了不同的大小写）。
             在PL/SQL中，如果您声明了一个名为Name的变量，并且还有一个名为NAME的列，Oracle会根据上下文来区分它们。但是，为了清晰和避免潜在的错误，最好不要这样做。
            最佳实践：
                避免在PL/SQL中使用与数据库对象名相同或相似的变量名。
                如果需要在PL/SQL中引用数据库对象，并且担心可能的名字冲突或混淆，可以使用表别名或明确的模式名来区分。
                在创建数据库对象时，考虑使用一致的命名约定，并且尽量避免使用大写字母（除非确实需要区分大小写）。
                总之，虽然Oracle在大多数情况下能够通过上下文来区分PL/SQL变量名和数据库对象名，但为了避免混淆和潜在的错误，最好还是遵循最佳实践，不要使用相同或相似的名字。

            下面是使用不同变量名的代码示例：
            sql
            DECLARE
              v_student_id NUMBER(22);
              v_student_name VARCHAR2(100);
            BEGIN
              v_student_id := 1;
              v_student_name := '张学友';
 
              UPDATE STUDENT
              SET NAME = v_student_name
              WHERE ID = v_student_id;
 
              COMMIT;
            END;

            在SQL的UPDATE语句中，SET子句用于指定要更新的列和新的值。如果PL/SQL块中有一个与数据库表列名同名的变量，并且在SET子句中错误地引用了这个变量（而没有使用任何前缀或别名来区分它），那么实际
            上并不会“将错误的值赋给其他列”。这里的“错误”更多地是指更新操作可能根本就不会按预期执行，或者更新的结果不是您所期望的。

            这里有几个关键点需要理解：

            SQL解析器的工作方式：当SQL解析器遇到UPDATE语句时，它会根据当前的上下文和解析规则来确定SET子句中的标识符是指向变量还是列。如果解析器无法确定（因为存在同名冲突），它可能会抛出一个错误
            或者尝试根据默认规则进行解析。
            变量和列名的区别：在PL/SQL块中声明的变量是局部的，它们只在该块的作用域内有效。而数据库表的列名是全局的，它们在整个数据库中都有效。因此，当您在SET子句中引用一个标识符时，您需要确保这个标识符是指向您想要更新的列，而不是一个同名的局部变量。
                如何避免冲突：为了避免这种冲突，您应该使用表别名或列别名来明确指定要更新的列。这样，即使变量名和列名相同，解析器也能够根据上下文正确地解析标识符。
            例如：

            sql
            DECLARE
              v_salary NUMBER := 5000; -- 这是一个局部变量
            BEGIN
              UPDATE employees e
              SET e.salary = e.salary + v_salary -- 这里明确使用了表别名e来指定列salary
              WHERE e.employee_id = 123;
              COMMIT;
            END;
            在这个例子中，即使有一个名为salary的局部变量，由于我们使用了表别名e来指定要更新的列，所以解析器能够正确地解析SET子句中的e.salary。

            然而，如果您错误地省略了表别名或列别名，并且变量名和列名完全相同，那么SQL解析器可能会尝试根据默认规则进行解析，这可能导致更新操作不执行或更新了错误的值（尽管这种情况比较少见，因为通常解析器会抛出错误以避免歧义）。但是，更常见的情况是解析器会抛出一个错误，提示存在标识符冲突。

            因此，为了避免这种潜在的冲突和错误，建议始终在UPDATE语句的SET子句中使用表别名或列别名来明确指定要更新的列。
            */
            #endregion


            //using (DataConnection db = new DataConnection(ProviderName.MySql, conMySQLString)) //也可以这样设置。注意的是此代码必须在调用的项目文件添加数据库dll，不然会报错读取不了。
            using (DataConnection db = new DataConnection(dataOptions))
            {
                DataConnection.TurnTraceSwitchOn();

                SetPrintSQL(db);

                //现在，当你使用linq2db执行查询时，它会在控制台中打印出生成的SQL语句
                Student student = new Student();
                //student.Id = 5;
                student.Name = "理想";

                //SQLServer设置了自增主键字段需要执行这条语句，不然会报错。不能正常插入Id，需要指定Id的值才行。其他数据库不需要此项设置。
                //int rows = db.Execute("SET IDENTITY_INSERT Student ON;");
                //int rows2 = db.Execute("SET IDENTITY_INSERT Student OFF;");
                //1:新增 
                //int insertCount = db.Insert(student);

                student.Id = 1;//默认id是0。
                student.Name = "张学友";
                var updateCount = db.Update(student);

                //LastQuery这个只是日志记录的Sql，不是可执行的SQL语句，所以没有太大意义，还需要自己转义一次替换成数据库可执行的SQL语句。
                //你想干什么？如果您使用日志sql，则它不是为执行而生成的，而是仅用于日志记录。
                string sql = db.LastQuery;
                //2:批量新增
                //db.Insert(new List<Student>());

                var studentModel = from s in db.GetTable<Student>() select s;

                var sql2 = db.GetTable<Student>().SqlText;
                //ToList调用在我们仍在使用时向数据库发送查询。
                var studentModel2 = db.GetTable<Student>().ToList();

                //打印生成执行的SQL语句：每次查询后，您都可以检查上下文。LastQuery查看语句；
                //包含使用当前连接发送到数据库的最后一个命令的文本。打印执行命令行的SQL语句
                return 0;
                //return insertCount;
            }
        }

        /// <summary>
        /// 打印局部的SQL语句
        /// </summary>
        /// <param name="db"></param>
        private static void SetPrintSQL(DataConnection db)
        {
            //跟using外的效果一样。但是一样会打印两次生成的Sql语句。需要添加trace.TraceInfoStep==TraceInfoStep.BeforeExecute判断级别是哪一个阶段的，排除掉冗余的sql输出，获得正确的sql输出语句。
            db.OnTraceConnection = trace =>
            {
                //在执行命令之前发生。
                //if (trace.TraceInfoStep == TraceInfoStep.BeforeExecute)
                //{
                //    Console.WriteLine("TraceInfoStep.BeforeExecute");
                //    Console.WriteLine(trace.SqlText);
                //}

                //打印出数据库表的属性名字和类型：ID NUMBER(22),NAME VARCHAR2(100),AGE VARCHAR2(100)
                string property = GetDatabaseProperty(db);

                string[] propertyArray = property.Split(',');

                //类型属性。
                string[] typeProperty = { };

                List<string[]> typePropertyList = new List<string[]>();
                if (propertyArray.Length>0)
                {
                    foreach (string item in propertyArray)
                    {
                        typeProperty=item.Split(' ');
                        typePropertyList.Add(typeProperty);
                    }
                }

                string commandText = trace.Command?.CommandText;
                DbParameterCollection dbParameterCollection = trace.Command?.Parameters;

                string sql = "DECLARE";
                string parameterName="";
                object parameterValue=null;
                string databaseSymbolPlaceHolder = "";
                string parameterNameStr = "";

                if (dbParameterCollection != null && dbParameterCollection.Count > 0)
                {
                    foreach (DbParameter param in dbParameterCollection)
                    {
                        parameterName = param.ParameterName;
                        parameterValue = param.Value;

                        if (db.DataProvider is OracleDataProvider)
                        {
                            #region Oracle输出的SQL字符串

                            #region Oracle的变量知识
                            //首先，Oracle 不支持像您所使用的 DECLARE 和 SET 语句这样的直接变量赋值方式（这是 MySQL 或 SQL Server 的语法）。
                            /*
                             首先，Oracle 不支持像您所使用的 DECLARE 和 SET 语句这样的直接变量赋值方式（这是 MySQL 或 SQL Server 的语法）。其次，您定义的 VARCHAR2(2) 对于存储中文字符（如“理想”）
                             来说太小了，因为每个中文字符在 UTF-8 编码中通常占用 3 个字节，而在 Oracle 的 VARCHAR2 中，字符长度是基于字符集计算的，但考虑到多字节字符，您仍然需要为它们提供足够的
                             空间。
                            以下是改正后的 SQL 代码，适用于 Oracle 数据库：

                            sql
                            -- 在 SQL*Plus 或其他支持 Oracle SQL 的工具中

                            -- 定义变量（注意：这里我们增加了变量的大小以适应中文字符）
                            VARIABLE Name VARCHAR2(10);

                            -- 赋值（在匿名 PL/SQL 块中）
                            BEGIN
                             :Name := '理想';  -- 使用 PL/SQL 的赋值语法 :=
                            END;

                            首先，VARIABLE 命令不是在 PL/SQL 块中使用的，它是在 SQLPlus 或其他类似的 Oracle 命令行工具中用来定义绑定变量的。其次，PL/SQL 块中的变量需要使用 DECLARE 关键字来声明，并且不能使用 : 前缀来引用，这是 SQLPlus 中绑定变量的语法。
                            以下是正确的 Oracle PL/SQL 代码，用于声明变量、赋值，并将值插入到表中：

                            plsql
                            -- 声明一个 PL/SQL 块
                            DECLARE
                              -- 声明变量
                              Name VARCHAR2(10);
                            BEGIN
                              -- 为变量赋值
                              Name := '理想';
 
                              -- 插入数据到表中
                              INSERT INTO STUDENT (NAME) VALUES (Name);
 
                              -- 提交事务（如果需要）
                              COMMIT;
                            END;

                            -- 插入数据到表中（确保 STUDENT 表存在且有一个名为 NAME 的列）
                            INSERT INTO STUDENT (NAME) VALUES (:Name);
                             */

                            /*
                             * 在Oracle PL/SQL中，使用绑定变量（通常称为参数化变量）的方式与你在这个代码片段中尝试的方式有所不同。在PL/SQL块中直接声明并使用绑定变量（如 :Name）通常不是正确的做法，尤其是在SQL语句中。绑定变量通常在SQL*Plus、SQL Developer等工具的脚本中使用，或者在应用程序代码中通过数据库接口（如JDBC、ODBC）使用，用于提高性能和安全性（防止SQL注入）。

                              在你的PL/SQL代码块中，你应该使用PL/SQL变量而不是绑定变量。下面是修改后的代码示例，它使用了PL/SQL变量来插入数据：
                                sql
                                DECLARE
                                  v_Name VARCHAR2(100);
                                BEGIN
                                  v_Name := '理想';
                                  INSERT INTO STUDENT (NAME) VALUES (v_Name);
                                  COMMIT;
                                END;
                                这里的改动包括：

                                将变量名从 :Name 改为 v_Name。在PL/SQL中，变量名通常以 v_、l_ 或 g_ 开头，以区分它们的作用域或类型（尽管这不是强制性的）。
                                移除了绑定变量的冒号前缀（:）。在PL/SQL中，变量不需要冒号前缀。
                                这段代码将正确执行，前提是你已经有一个名为 STUDENT 的表，并且该表有一个名为 NAME 的列，数据类型兼容 VARCHAR2(100)。

                                如果你需要在SQL*Plus或SQL Developer中执行参数化的SQL语句（例如，从命令行接受参数），你应该使用 VARIABLE 命令声明变量，然后使用 EXECUTE 或 EXEC 命令执行带有这些变量的SQL语句。但是，这种方法不适用于PL/SQL块内部，只适用于单独的SQL语句执行环境。


                               首先，您不需要在PL/SQL块中多次使用DECLARE关键字；所有变量声明都应该放在单个DECLARE部分中。其次，您的UPDATE语句与前面的Name变量赋值之间缺少分号（;），这是PL/SQL中用来分隔语句的。
                               样例：
                                DECLARE Id NUMBER(22);Name VARCHAR2(100);
                                BEGIN Id :=1;
                                      Name:='张学友';
                                UPDATE
	                                STUDENT
                                SET
	                                STUDENT.NAME = Name
                                WHERE
	                                STUDENT.ID = Id;

                                COMMIT;
                                END;
                             */
                            #endregion

                            databaseSymbolPlaceHolder = ":";
                           
                            if (parameterValue != null && parameterValue is string)
                            {
                                foreach (string[] typeProperty2 in typePropertyList)
                                {
                                    if (typeProperty2[0].Contains(parameterName.ToUpper()))
                                    {
                                        sql += $@" v_{parameterName} {typeProperty2[1]};";
                                        parameterNameStr += $"v_{parameterName}:='{parameterValue}';";
                                    }
                                }

                                //因为不好构造声明变量的SQL语句的数据库声明类型，直接替换掉对应的参数值即可。
                                //commandText = commandText.Replace($":{parameterName}", $"'{param.Value}'");
                            }
                            else 
                            {
                                //int类型或者其他都不用带单引号'
                                //commandText += ";\n" + $@"DECLARE {parameterName} {param.DbType}; BEGIN {parameterName} {databaseSymbolPlaceholder}={param.Value}";

                                foreach (string[] typeProperty2 in typePropertyList)
                                {
                                    if (typeProperty2[0].Contains(parameterName.ToUpper()))
                                    {
                                        if (typeProperty2[0].Contains(parameterName.ToUpper()))
                                        {
                                            sql += $@" v_{parameterName} {typeProperty2[1]};";
                                            parameterNameStr +=$"v_{parameterName}:={parameterValue};";
                                        }
                                    }
                                }
                            }

                            //因为不好构造声明变量的SQL语句的数据库声明类型，直接替换掉对应的参数值即可。
                            //commandText = commandText.Replace($":{parameterName}", $"{param.Value}");

                            #endregion
                        }
                        else if (db.DataProvider is MySqlDataProvider)
                        {
                            #region MySQL驱动输出的SQL字符串
                            /*
                             * MySQL变量赋值的标准例子如下：
                             --给变量赋值
                             SET @my_variable = 10;

                             --输出变量的值
                             SELECT @my_variable;
                            */

                            /*
                             * 在 MySQL 中，确实没有像 SQLServer 那样的 DECLARE 和 SET 语句用于在 SQL 脚本层面直接定义和赋值变量（至少不是以您可能想到的那种方式）。在 MySQL 中，变量的处理有所不同，特别是在存储过程、函数和触发器之外。
                                在 MySQL 中，用户定义的变量（也称为会话变量）可以在 SQL 语句中通过 @variable_name 语法来引用，并且可以通过 SET 或 SELECT ... INTO 语句来赋值。但是，这些变量没有显式的声明步骤；它们是在赋值时隐式创建的。

                                以下是在 MySQL 中定义（实际上是赋值）和使用变量的示例：
                                sql
                                -- 赋值变量（注意：这里不需要 DECLARE）
                                SET @Name = '理想';
 
                                -- 或者使用 SELECT INTO 语法（如果变量值来自查询结果）
                                -- SELECT '理想' INTO @Name; -- 这通常用于从查询中获取值
 
                                -- 使用变量（在 SQL 语句中通过 @variable_name 引用）
                                INSERT INTO STUDENT (NAME) VALUES (@Name);
                                然而，需要注意的是，在 MySQL 的存储过程、函数和触发器中，变量的处理是不同的。在这些上下文中，您确实需要使用 DECLARE 语句来定义局部变量，并使用 SET 或 SELECT ... INTO 语句来赋值：
                                sql
                                DELIMITER //
 
                                CREATE PROCEDURE InsertStudentName()
                                BEGIN
                                    DECLARE Name VARCHAR(10); -- 在存储过程中声明变量
                                    SET Name = '理想';        -- 为变量赋值
 
                                    -- 使用变量
                                    INSERT INTO STUDENT (NAME) VALUES (Name);
                                END //
                            */
                            databaseSymbolPlaceHolder = "@";

                            if (param.Value != null && param.Value is string)
                            {
                                commandText += ";\n" + $@"SET {databaseSymbolPlaceHolder}{parameterName}='{param.Value}'";
                            }
                            else
                            {
                                //int类型或者其他都不用带单引号'
                                commandText += ";\n" + $@"SET {databaseSymbolPlaceHolder}{parameterName}={param.Value}";
                            }
                            #endregion
                        }

                    };

                    sql += $@" BEGIN {parameterNameStr}";
                    commandText = sql + commandText.Replace($":", "v_") + ";\nCOMMIT;\nEND;";
                }

                var cmd = trace.CommandText;//这个都是空，没有用的属性。
                                            //在执行命令后发生。取最后一次，前面执行命令之前忽略不计。就可以拿到正确的的打印SQL一次。
                if (trace.TraceInfoStep == TraceInfoStep.AfterExecute)
                {
                    // 检查 DataConnection 的 DataProvider 是否是 MySqlDataProvider
                    if (db.DataProvider is MySqlDataProvider)
                    {
                        Console.WriteLine("TraceInfoStep.AfterExecute");

                        //过滤字符串，分两种，用方法过滤或者自己拼接字符串。下面是第一种过滤字符串的方法。
                        //string mySql = trace.SqlText.Replace("DECLARE", "");
                        //Console.WriteLine(mySql);

                        //第二种过滤字符串。
                        Console.WriteLine(commandText);
                    }
                    else
                    {
                        Console.WriteLine("TraceInfoStep.AfterExecute");
                        //Console.WriteLine(trace.SqlText);
                        //第二种过滤字符串。
                        Console.WriteLine(commandText);
                    }
                }
            };
        }

        /// <summary>
        /// 打印全局的可执行的SQL语句。
        /// </summary>
        private static void PrintSQL()
        {
            // 文心一言生成的注释：配置linq2db日志记录器
            // 您可以使用TraceSwitchConnection启用跟踪，并使用OnTraceConnection接收有关每个操作的跟踪信息，然后记录语句。
            DataConnection.TurnTraceSwitchOn();

            /*
             在 linq2db 中，DataConnection.TraceSwitch 是一个静态的 TraceSwitch 属性，它用于控制是否启用跟踪以及跟踪的详细级别。然而，直接通过 DataConnection.TraceSwitch = new TraceSwitch("", "")
             这样的代码来设置跟踪通常是不正确的，因为 TraceSwitch 的构造函数通常需要一个描述性的名称和一个默认的级别字符串。
             但是，对于linq2db 的用途，你不需要创建一个新的 TraceSwitch 实例来赋值给 DataConnection.TraceSwitch。相反，你应该直接设置 DataConnection.TraceSwitch.Level 属性来启用跟踪并指定跟踪级
             别。
                //可选：添加自定义的 TraceListener 来捕获跟踪输出
                var textListener = new TextWriterTraceListener(Console.Out);
                Trace.Listeners.Add(textListener);
 
              现在，当你使用 DataConnection 执行数据库操作时，跟踪信息将被输出
              在这个例子中，我们没有创建一个新的 TraceSwitch 实例，而是直接修改了 DataConnection.TraceSwitch 的 Level 属性。这是启用 linq2db 跟踪的正确方法。
              如果你确实想要创建一个新的 TraceSwitch 实例（尽管在 linq2db 的上下文中这不是必需的），你应该这样做：
              var myTraceSwitch = new TraceSwitch("MyTraceSwitch", "Verbose");
              但是，请注意，这个新创建的 TraceSwitch 实例不会被 linq2db 自动使用。
              你需要手动管理它，并且它不会与 DataConnection.TraceSwitch 交互。
              然而，对于 linq2db 的跟踪来说，重要的是要使用 DataConnection.TraceSwitch，并且只设置它的 Level 属性来启用跟踪。
              最后，请确保你的应用程序有适当的权限来写入跟踪信息到指定的输出（例如，控制台、文件或日志服务）。如果跟踪输出没有按预期出现，请检查是否有任何权限问题或配置错误。
             */
            //DataConnection.TraceSwitch = new TraceSwitch("MyTraceSwitch", "Verbose");//这个设置不正确，应该这样它的Level级别即可。
            // 在应用程序的初始化代码中设置跟踪级别
            //DataConnection.TraceSwitch.Level = TraceLevel.Verbose; // 或者其他你需要的级别，如 Info, Warning, Error

            /*
            * 这段代码的作用是向全局的 Trace 类添加一个监听器，该监听器会将跟踪信息输出到控制台。这是 .NET 框架中标准的跟踪和诊断机制之一。
              然而，问题在于 LINQ2DB 可能并不使用 System.Diagnostics.Trace 类来输出其跟踪或日志信息。相反，它可能使用自己的日志记录机制，或者依赖于其他日志框架（如 NLog、log4net 等），或者将信息
              输出到调试器（如果您在调试模式下运行应用程序）。因此，即使您添加了 TextWriterTraceListener，如果 LINQ2DB 不使用 Trace 类，您也不会在控制台中看到任何来自 LINQ2DB 的跟踪信息。
              配置 LINQ2DB 的跟踪：
              LINQ2DB 允许通过配置来控制跟踪的输出。这通常是通过设置 DataConnection 的 OnTraceConnection 属性或使用 LINQ2DB 提供的配置选项来完成的。这些配置选项允许您指定跟踪信息的输出目标，而不是
              依赖于全局的 Trace 类。
             */
            //可选：添加自定义的 TraceListener 来捕获跟踪输出,以下代码没有用，因为如果 LINQ2DB 没有使用 Trace 类来输出其跟踪信息，那么添加到 Trace.Listeners 的监听器就不会捕获到 LINQ2DB 的跟踪信息。
            //var textListener = new TextWriterTraceListener(Console.Out);
            //Trace.Listeners.Add(textListener);

            /*
             * 在这个例子中，我们为 DataConnection.DefaultOnTraceConnection 提供了一个匿名函数（lambda 表达式），它简单地将跟踪信息输出到控制台。你可以根据需要修改这个函数来处理跟踪信息，比如将其写入日志文件、发送到远程日志服务或执行其他自定义操作。
               请注意，DataConnection.DefaultOnTraceConnection 是一个静态属性，因此它设置的是全局的默认行为。如果你只想为特定的 DataConnection 实例启用跟踪回调，你可以在该实例上设置
               OnTraceConnection 属性，而不是修改全局的默认设置。
               另外，请确保你的回调函数不会引入性能瓶颈，特别是在高并发或高性能要求的场景中。跟踪信息通常用于调试和监控目的，因此它们不应该对正常的应用程序性能产生显著影响。
             */

            // 可以选择将日志输出到控制台: category=跟踪消息类别。
            DataConnection.WriteTraceLine = (message, messageCategory, traceLevel) => { 

                Console.WriteLine(message, messageCategory, traceLevel); 
            };

            //或者下面的输出代码。
            // 设置默认的跟踪连接回调
            //DataConnection.DefaultOnTraceConnection = trace =>
            //{
            //    // 这里是处理跟踪信息的代码
            //    // trace 参数包含了从 linq2db 生成的跟踪信息

            //    if (trace.TraceInfoStep == TraceInfoStep.AfterExecute)
            //    {
            //        Console.WriteLine(trace.SqlText); // 例如，将跟踪信息输出到控制台
            //    }
            //};
        }

        /// <summary>
        /// 获取数据库属性值,这个基本不能实现因为在C#中都是C#的类型，而没有直接数据库的类型。可以忽略不考虑的元素了。
        /// </summary>
        private static string GetDatabaseProperty(DataConnection db)
        {
            var tableName = "Student".ToUpper();

            //sql里面结束标识不能带有;，不然执行查询的时候会报Oracle的数据库错误。
            //column_name:某表列名(varchar2(30))；data_type:某表列的数据类型(varchar2(106))；data_length：某表列的长度（number)；
            string sql = $"select column_name,data_type ,data_length,data_precision,data_scale from user_tab_columns where table_name='{tableName}'";

            DataTable dt = OracleHelperNew.ExcuteDataTable(db.ConnectionString,sql);

            string strSQL="";
            for (int j = 0; j < dt.Rows.Count; j++)
            {
                if (j == dt.Rows.Count - 1)
                {
                    strSQL += dt.Rows[j]["column_name"].ToString() + " " + dt.Rows[j]["data_type"].ToString() + "(" +dt.Rows[j]["data_length"].ToString() + ")";
                }
                else
                {
                    strSQL += dt.Rows[j]["column_name"].ToString() + " " + dt.Rows[j]["data_type"].ToString() + "(" + dt.Rows[j]["data_length"].ToString() + ")" + ",";
                }
            }

            return strSQL;
        }
    }
}
